package script

import (
	"os"
	"path/filepath"
	"sync"
	"time"

	"tinyGW/pkg/plugin/io"

	"github.com/fsnotify/fsnotify"
	"go.uber.org/zap"
)

// PluginManager 插件管理器 - 负责加载、缓存和热更新插件
type PluginManager struct {
	plugins     map[string]Runner    // 插件名称 -> Runner 实例
	pluginOrder []string             // 插件检测顺序（按目录名排序）
	mutex       sync.RWMutex         // 读写锁
	watcher     *fsnotify.Watcher    // 文件监听器
	pluginDir   string               // 插件目录路径
	lastReload  map[string]time.Time // 插件最后重载时间（防抖）
	reloadMutex sync.Mutex           // 重载互斥锁
}

var (
	// 全局插件管理器实例
	globalPluginManager *PluginManager
	initOnce            sync.Once
)

// GetPluginManager 获取全局插件管理器实例（单例）
func GetPluginManager() *PluginManager {
	initOnce.Do(func() {
		manager := &PluginManager{
			plugins:     make(map[string]Runner),
			pluginOrder: make([]string, 0),
			lastReload:  make(map[string]time.Time),
			pluginDir:   filepath.Join(io.GetCurrentPath(), "plugin"),
		}

		// 初始加载所有插件
		if err := manager.LoadAllPlugins(); err != nil {
			zap.S().Errorf("初始加载插件失败: %v", err)
		}

		// 启动文件监听
		if err := manager.StartWatching(); err != nil {
			zap.S().Warnf("启动插件目录监听失败: %v", err)
		}

		globalPluginManager = manager
	})
	return globalPluginManager
}

// LoadAllPlugins 加载所有插件到内存
func (pm *PluginManager) LoadAllPlugins() error {
	pm.mutex.Lock()
	defer pm.mutex.Unlock()

	zap.S().Info("开始加载协议插件...")

	entries, err := os.ReadDir(pm.pluginDir)
	if err != nil {
		return err
	}

	loadedCount := 0
	for _, entry := range entries {
		if !entry.IsDir() {
			continue
		}

		pluginName := entry.Name()
		luaFile := filepath.Join(pm.pluginDir, pluginName, pluginName+".lua")

		// 检查 .lua 文件是否存在
		if _, err := os.Stat(luaFile); os.IsNotExist(err) {
			continue
		}

		// 加载插件
		runner := NewLuaRunner()
		if err := runner.Open(pluginName); err != nil {
			zap.S().Warnf("加载插件 %s 失败: %v", pluginName, err)
			continue
		}

		pm.plugins[pluginName] = runner
		pm.pluginOrder = append(pm.pluginOrder, pluginName)
		loadedCount++

		zap.S().Infof("✓ 加载协议插件: %s", pluginName)
	}

	zap.S().Infof("插件加载完成，共 %d 个协议插件", loadedCount)
	return nil
}

// ReloadPlugin 重新加载指定插件
func (pm *PluginManager) ReloadPlugin(pluginName string) error {
	pm.mutex.Lock()
	defer pm.mutex.Unlock()

	// 防抖：1秒内只重载一次
	pm.reloadMutex.Lock()
	lastTime, exists := pm.lastReload[pluginName]
	now := time.Now()
	if exists && now.Sub(lastTime) < time.Second {
		pm.reloadMutex.Unlock()
		return nil
	}
	pm.lastReload[pluginName] = now
	pm.reloadMutex.Unlock()

	zap.S().Infof("重新加载插件: %s", pluginName)

	// 关闭旧的 runner
	if oldRunner, exists := pm.plugins[pluginName]; exists {
		oldRunner.Close()
	}

	// 创建新的 runner
	runner := NewLuaRunner()
	if err := runner.Open(pluginName); err != nil {
		zap.S().Errorf("重新加载插件 %s 失败: %v", pluginName, err)
		return err
	}

	pm.plugins[pluginName] = runner

	// 如果是新插件，添加到顺序列表
	found := false
	for _, name := range pm.pluginOrder {
		if name == pluginName {
			found = true
			break
		}
	}
	if !found {
		pm.pluginOrder = append(pm.pluginOrder, pluginName)
	}

	zap.S().Infof("✓ 插件 %s 热更新成功", pluginName)
	return nil
}

// DetectProtocol 使用内存中的插件检测协议
func (pm *PluginManager) DetectProtocol(data []byte) (string, Runner) {
	pm.mutex.RLock()
	defer pm.mutex.RUnlock()

	zap.S().Debugf("开始协议检测，数据长度: %d, 插件数量: %d", len(data), len(pm.pluginOrder))

	// 按顺序遍历所有插件
	for _, pluginName := range pm.pluginOrder {
		runner, exists := pm.plugins[pluginName]
		if !exists {
			zap.S().Debugf("插件 %s 不存在于缓存中", pluginName)
			continue
		}

		zap.S().Debugf("尝试使用插件 %s 检测协议...", pluginName)

		// 检测协议
		if runner.DetectProtocol(data) {
			zap.S().Infof("✓ 协议检测成功: %s", pluginName)
			// 注意：不能直接返回 runner，因为它会被多个 goroutine 共享
			// 需要为每个连接创建新的 runner 实例
			newRunner := NewLuaRunner()
			if err := newRunner.Open(pluginName); err != nil {
				zap.S().Errorf("创建协议 %s 的新实例失败: %v", pluginName, err)
				continue
			}
			return pluginName, newRunner
		} else {
			zap.S().Debugf("插件 %s 检测失败", pluginName)
		}
	}

	zap.S().Debugf("所有插件都无法识别该协议")
	return "", nil
}

// GetPluginList 获取所有已加载的插件名称
func (pm *PluginManager) GetPluginList() []string {
	pm.mutex.RLock()
	defer pm.mutex.RUnlock()

	result := make([]string, len(pm.pluginOrder))
	copy(result, pm.pluginOrder)
	return result
}

// StartWatching 启动文件监听，支持热更新
func (pm *PluginManager) StartWatching() error {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	pm.watcher = watcher

	// 监听主插件目录
	if err := watcher.Add(pm.pluginDir); err != nil {
		return err
	}

	// 监听每个插件的子目录
	entries, err := os.ReadDir(pm.pluginDir)
	if err == nil {
		for _, entry := range entries {
			if entry.IsDir() {
				subDir := filepath.Join(pm.pluginDir, entry.Name())
				watcher.Add(subDir)
			}
		}
	}

	// 启动监听 goroutine
	go pm.watchLoop()

	zap.S().Info("插件目录监听已启动，支持热更新")
	return nil
}

// watchLoop 文件监听循环
func (pm *PluginManager) watchLoop() {
	for {
		select {
		case event, ok := <-pm.watcher.Events:
			if !ok {
				return
			}

			// 只处理 .lua 文件的写入和创建事件
			if filepath.Ext(event.Name) != ".lua" {
				continue
			}

			if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
				// 从路径提取插件名称
				pluginName := filepath.Base(filepath.Dir(event.Name))
				luaFileName := filepath.Base(event.Name)

				// 检查文件名是否与目录名匹配
				if luaFileName == pluginName+".lua" {
					zap.S().Infof("检测到插件文件变化: %s", event.Name)

					// 延迟一下再重载，避免文件还在写入
					go func(name string) {
						time.Sleep(100 * time.Millisecond)
						pm.ReloadPlugin(name)
					}(pluginName)
				}
			}

		case err, ok := <-pm.watcher.Errors:
			if !ok {
				return
			}
			zap.S().Errorf("文件监听错误: %v", err)
		}
	}
}

// Close 关闭插件管理器
func (pm *PluginManager) Close() {
	pm.mutex.Lock()
	defer pm.mutex.Unlock()

	// 关闭所有插件
	for name, runner := range pm.plugins {
		runner.Close()
		zap.S().Debugf("关闭插件: %s", name)
	}

	// 关闭文件监听器
	if pm.watcher != nil {
		pm.watcher.Close()
	}

	zap.S().Info("插件管理器已关闭")
}

// GetPluginCount 获取已加载的插件数量
func (pm *PluginManager) GetPluginCount() int {
	pm.mutex.RLock()
	defer pm.mutex.RUnlock()
	return len(pm.plugins)
}
